package com.jweb.framework.ws.soap.axiom;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import javax.activation.DataHandler;
import javax.xml.stream.XMLStreamException;
import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.impl.MIMEOutputUtils;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPMessage;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.ws.mime.Attachment;
import org.springframework.ws.soap.AbstractSoapMessage;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.support.SoapUtils;
import org.springframework.ws.transport.TransportOutputStream;
import org.w3c.dom.Document;
/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：
 */
public class AxiomSoapMessage extends AbstractSoapMessage {
	
	private SOAPMessage axiomMessage ;
	
	private final SOAPFactory axiomFactory ;
	
	private final Attachments attachments ;
	
	private final boolean payloadCaching ;
	
	private AxiomSoapEnvelope envelope ;
	
	private String soapAction ;
	
	private final boolean langAttributeOnSoap11FaultString ;
	
	private OMOutputFormat outputFormat ;
	
	
	public AxiomSoapMessage(SOAPFactory soapFactory){
		this(soapFactory , true , true);
	}
	//在创建connect时新建AxiomSoapMessage
	public AxiomSoapMessage(SOAPFactory soapFactory , boolean payloadCaching , boolean langAttributeOnSoap11FaultString) {
		
		SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();//<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body></soapenv:Body></soapenv:Envelope>
		this.axiomFactory = soapFactory ;
		this.axiomMessage = this.axiomFactory.createSOAPMessage(soapEnvelope, soapEnvelope.getBuilder());
		this.attachments = new Attachments();
		this.payloadCaching = payloadCaching ;
		this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString ;
		this.soapAction = "\"\"";
	}
	
	public AxiomSoapMessage(SOAPMessage soapMessage , String soapAction , boolean payloadCaching , boolean langAttributeOnSoap11FaultString){
		this(soapMessage , new Attachments() , soapAction , payloadCaching , langAttributeOnSoap11FaultString);
	}
	
	public AxiomSoapMessage(SOAPMessage soapMessage , Attachments attachments , String soapAction , boolean payloadCaching , boolean langAttributeOnSoap11FaultString){
		Assert.notNull(soapMessage , "'soapMessage' must not be null");
		Assert.notNull(attachments , "'attachments' must not be null");
		this.axiomMessage = soapMessage ;
		this.axiomFactory = (SOAPFactory) soapMessage.getSOAPEnvelope().getOMFactory();
		this.attachments = attachments ;
		if(!StringUtils.hasLength(soapAction)){
			soapAction = "\"\"";
		}
		this.soapAction = soapAction ;
		this.payloadCaching = payloadCaching ;
		this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString ;
	}
	
	
	public final SOAPMessage getAxiomMessage(){
		return axiomMessage ;
	}
	
	public final void setAxiomMessage(SOAPMessage axiomMessage) {
		Assert.notNull(axiomMessage, "'axiomMessage' must not be null");
		this.axiomMessage = axiomMessage ;
		this.envelope = null ;
		this.soapAction = "\"\"";
	}
	
	public void setOutputFormat(OMOutputFormat outputFormat) {
		this.outputFormat = outputFormat ;
	}
	
	public SoapEnvelope getEnvelope(){
		if(envelope == null){
			try {
				envelope = new AxiomSoapEnvelope(axiomMessage.getSOAPEnvelope() , axiomFactory , payloadCaching , langAttributeOnSoap11FaultString);
			} catch (SOAPProcessingException ex) {
				throw new AxiomSoapEnvelopeException(ex);
			}
		}
		return envelope ;
	}
	
	public String getSoapAction(){
		return soapAction ;
	}
	
	public void setSoapAction(String soapAction) {
		soapAction = SoapUtils.escapeAction(soapAction);
		this.soapAction = soapAction ;
	}
	
	public boolean isXopPackage(){
		try {
			
			return "application/xop+xml".equals(attachments.getAttachmentSpecType());
			
		} catch (NullPointerException e) {
			return false;
		}
	}
	
	public boolean convertToXopPackage() {
		return false ;
	}
	
	public Attachment getAttachment(String contentId) {
		
		Assert.hasLength(contentId , "contentId must not be empty");
		if(contentId.startsWith("<") && contentId.endsWith(">")){
			contentId = contentId.substring(1 , contentId.length() - 1);
		}
		DataHandler dataHandler = attachments.getDataHandler(contentId);
		
		return dataHandler!=null ? new AxiomAttachment(contentId , dataHandler) : null ;
	}
	
	public Iterator<Attachment> getAttachments(){
		return new AxiomAttachmentIterator();
	}
	
	public Attachment addAttachment(String contentId , DataHandler dataHandler) {
		Assert.hasLength(contentId, "contentId must not be empty");
		Assert.notNull(dataHandler, "dataHandler must not be null");
		attachments.addDataHandler(contentId, dataHandler);
		return new AxiomAttachment(contentId , dataHandler);
	}
	
	public void writeTo(OutputStream outputStream) throws IOException {
		
		try {
			OMOutputFormat outputFormat = getOutputFormat();
			if(outputStream instanceof TransportOutputStream){ // outputStream={org.springframework.ws.transport.AbstractSenderConnection$RequestTransportOutputStream}
				TransportOutputStream transportOutputStream = (TransportOutputStream) outputStream ;
				String contentType = outputFormat.getContentType();  // contentType="text/xml"
				if(!(outputFormat.isDoingSWA() && !outputFormat.isOptimized())){ // true
					String charsetEncoding = axiomMessage.getCharsetEncoding(); // "UTF-8"
					contentType += "; charset=" + charsetEncoding ; // contentType="text/xml; charset=UTF-8"
				}
				SoapVersion version = getVersion();
				if(SoapVersion.SOAP_11 == version){ // true
					transportOutputStream.addHeader("SOAPAction", soapAction);   // """"
					transportOutputStream.addHeader("Accept", version.getContentType());// "text/xml"
				}
				else if(SoapVersion.SOAP_12 == version){
					contentType += "; action=" + soapAction ;
					transportOutputStream.addHeader("Accept", version.getContentType());
				}
				transportOutputStream.addHeader("Content-Type", contentType);
			}
			
			if(!(outputFormat.isOptimized()) & outputFormat.isDoingSWA()){// false
				writeSwAMessage(outputStream , outputFormat);
			}
			/*else if(Utils.OSGI){// false
				XMLStreamWriter xmlStreamWriter = new MTOMXMLStreamWriter1(outputStream, outputFormat);
				if(this.payloadCaching){
					this.axiomMessage.serialize(xmlStreamWriter , true);
				}
				else {
					try{
						this.axiomMessage.serialize(xmlStreamWriter , false);
					} finally {
						xmlStreamWriter.close();
					}
				}
			}*/
			if(this.payloadCaching){// true
				/*
				 * OMContainer:Captures夺取 the operations related to containment控制 shared by both a document and an element.
				 * 		Exposes显露 the ability to add,find,and iterate over the children of a document or element .
				 * 
				 * serialize(OutputStream,OMOutputFormat):Serialize the node with caching enabled.
				 * 
				 */
				axiomMessage.serialize(outputStream , outputFormat);//此处将正文信息写入到outputStream流中。 aixomMessage=org.apache.axiom.soap.impl.llom.SOAPMessageImpl
			}
			else {
				axiomMessage.serializeAndConsume(outputStream , outputFormat);
			}
			
			outputStream.flush();
		} catch (XMLStreamException e) {
			throw new AxiomSoapMessageException("Could not write message to OutputStream: " + e.getMessage() , e);
		}
		catch (OMException e){
			throw new AxiomSoapMessageException("Could not write message to OutputStream: " + e.getMessage() , e);
		}
		
	}
	
	private OMOutputFormat getOutputFormat() {
		
		if(outputFormat != null){
			return outputFormat ;
		}
		else {
			String charSetEncoding = axiomMessage.getCharsetEncoding(); // "UTF-8"
			
			OMOutputFormat outputFormat = new OMOutputFormat();
			outputFormat.setCharSetEncoding(charSetEncoding);
			outputFormat.setSOAP11(getVersion()==SoapVersion.SOAP_11);
			
			if(isXopPackage()){// false
				outputFormat.setDoOptimize(true);
			}
			else if(!attachments.getContentIDSet().isEmpty()){// false
				outputFormat.setDoingSWA(true);
			}
			
			return outputFormat ;
		}
	}

	private void writeSwAMessage(OutputStream outputStream, OMOutputFormat format) throws XMLStreamException , UnsupportedEncodingException {
		
		StringWriter writer = new StringWriter();
		/*if(Utils.OSGI){
			XMLStreamWriter xmlStreamWriter = new MTOMXMLStreamWriter(XMLUtils.createXMLWriter(writer));
			SOAPEnvelope soapEnvelope = this.axiomMessage.getSOAPEnvelope();
			if(this.payloadCaching){
				soapEnvelope.serialize(xmlStreamWriter , true);
			}
			else {
				soapEnvelope.serialize(xmlStreamWriter , false);
			}
			MIMEOutputUtils.writeSOAPWithAttachmentsMessage(writer, outputStream, attachments, format);
		}
		else {*/
			SOAPEnvelope soapEnvelope = this.axiomMessage.getSOAPEnvelope();
			if(this.payloadCaching){
				soapEnvelope.serialize(writer , format);
			}
			else {
				soapEnvelope.serializeAndConsume(writer , format);
			}
			MIMEOutputUtils.writeSOAPWithAttachmentsMessage(writer, outputStream, attachments, format);
		/*}*/
		
	}

	
	public String toString(){
		StringBuilder builder = new StringBuilder("AxiomSoapMessage");
		if(payloadCaching){
			try {
				SOAPEnvelope envelope = axiomMessage.getSOAPEnvelope();//envelope=<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><p0:queryAcctDateResponse xmlns:p0="http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0" xmlns:p1="http://gsp.bankcomm.com" xmlns:p2="http://gsp.service.bankcomm.com/QueryAcctDateService"><p1:replyInformation><p1:responseType>N</p1:responseType><p1:responseCode>SC0000</p1:responseCode></p1:replyInformation><p2:TOA><p2:TOA_CICS></p2:TOA_CICS><p2:TOA_CUSRPOS>0000</p2:TOA_CUSRPOS><p2:TOA_TMNO></p2:TOA_TMNO><p2:TOA_TMSEQNO>0</p2:TOA_TMSEQNO><p2:TOA_SUBSTRCODE>0000</p2:TOA_SUBSTRCODE><p2:TOA_SUBSAPCODE>00</p2:TOA_SUBSAPCODE><p2:TOA_TRMMO></p2:TOA_TRMMO><p2:TOA_TRDATE>20140612</p2:TOA_TRDATE><p2:TOA_TRTIME>110154</p2:TOA_TRTIME><p2:TOA_ACDATE>20160201</p2:TOA_ACDATE><p2:TOA_JRNNO>000000000</p2:TOA_JRNNO><p2:TOA_VCHNO>EIRIBB10000</p2:TOA_VCHNO><p2:TOA_PAGEIND>0</p2:TOA_PAGEIND><p2:TOA_COMPIND>0</p2:TOA_COMPIND><p2:TOA_ENCRIND>0</p2:TOA_ENCRIND><p2:TOA_TRBR>310800</p2:TOA_TRBR><p2:TOA_AUTHLOG></p2:TOA_AUTHLOG><p2:TOA_FINFLAG></p2:TOA_FINFLAG><p2:TOA_AUTH>00</p2:TOA_AUTH><p2:TOA_SUP1ID></p2:TOA_SUP1ID><p2:TOA_SUP2ID></p2:TOA_SUP2ID><p2:TOA_BACKUP></p2:TOA_BACKUP><p2:TOA_MSGLEN>0</p2:TOA_MSGLEN></p2:TOA></p0:queryAcctDateResponse></soapenv:Body></soapenv:Envelope>
				if(envelope != null){
					SOAPBody body = envelope.getBody();//body=<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><p0:queryAcctDateResponse xmlns:p0="http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0" xmlns:p1="http://gsp.bankcomm.com" xmlns:p2="http://gsp.service.bankcomm.com/QueryAcctDateService"><p1:replyInformation><p1:responseType>N</p1:responseType><p1:responseCode>SC0000</p1:responseCode></p1:replyInformation><p2:TOA><p2:TOA_CICS></p2:TOA_CICS><p2:TOA_CUSRPOS>0000</p2:TOA_CUSRPOS><p2:TOA_TMNO></p2:TOA_TMNO><p2:TOA_TMSEQNO>0</p2:TOA_TMSEQNO><p2:TOA_SUBSTRCODE>0000</p2:TOA_SUBSTRCODE><p2:TOA_SUBSAPCODE>00</p2:TOA_SUBSAPCODE><p2:TOA_TRMMO></p2:TOA_TRMMO><p2:TOA_TRDATE>20140612</p2:TOA_TRDATE><p2:TOA_TRTIME>110154</p2:TOA_TRTIME><p2:TOA_ACDATE>20160201</p2:TOA_ACDATE><p2:TOA_JRNNO>000000000</p2:TOA_JRNNO><p2:TOA_VCHNO>EIRIBB10000</p2:TOA_VCHNO><p2:TOA_PAGEIND>0</p2:TOA_PAGEIND><p2:TOA_COMPIND>0</p2:TOA_COMPIND><p2:TOA_ENCRIND>0</p2:TOA_ENCRIND><p2:TOA_TRBR>310800</p2:TOA_TRBR><p2:TOA_AUTHLOG></p2:TOA_AUTHLOG><p2:TOA_FINFLAG></p2:TOA_FINFLAG><p2:TOA_AUTH>00</p2:TOA_AUTH><p2:TOA_SUP1ID></p2:TOA_SUP1ID><p2:TOA_SUP2ID></p2:TOA_SUP2ID><p2:TOA_BACKUP></p2:TOA_BACKUP><p2:TOA_MSGLEN>0</p2:TOA_MSGLEN></p2:TOA></p0:queryAcctDateResponse></soapenv:Body>
					if(body != null){
						OMElement bodyElement = body.getFirstElement();//bodyElement=<p0:queryAcctDateResponse xmlns:p0="http://gsp.service.bankcomm.com/abstraction/atomic/technical.QueryAcctDateService/1_0" xmlns:p1="http://gsp.bankcomm.com" xmlns:p2="http://gsp.service.bankcomm.com/QueryAcctDateService"><p1:replyInformation><p1:responseType>N</p1:responseType><p1:responseCode>SC0000</p1:responseCode></p1:replyInformation><p2:TOA><p2:TOA_CICS></p2:TOA_CICS><p2:TOA_CUSRPOS>0000</p2:TOA_CUSRPOS><p2:TOA_TMNO></p2:TOA_TMNO><p2:TOA_TMSEQNO>0</p2:TOA_TMSEQNO><p2:TOA_SUBSTRCODE>0000</p2:TOA_SUBSTRCODE><p2:TOA_SUBSAPCODE>00</p2:TOA_SUBSAPCODE><p2:TOA_TRMMO></p2:TOA_TRMMO><p2:TOA_TRDATE>20140612</p2:TOA_TRDATE><p2:TOA_TRTIME>110154</p2:TOA_TRTIME><p2:TOA_ACDATE>20160201</p2:TOA_ACDATE><p2:TOA_JRNNO>000000000</p2:TOA_JRNNO><p2:TOA_VCHNO>EIRIBB10000</p2:TOA_VCHNO><p2:TOA_PAGEIND>0</p2:TOA_PAGEIND><p2:TOA_COMPIND>0</p2:TOA_COMPIND><p2:TOA_ENCRIND>0</p2:TOA_ENCRIND><p2:TOA_TRBR>310800</p2:TOA_TRBR><p2:TOA_AUTHLOG></p2:TOA_AUTHLOG><p2:TOA_FINFLAG></p2:TOA_FINFLAG><p2:TOA_AUTH>00</p2:TOA_AUTH><p2:TOA_SUP1ID></p2:TOA_SUP1ID><p2:TOA_SUP2ID></p2:TOA_SUP2ID><p2:TOA_BACKUP></p2:TOA_BACKUP><p2:TOA_MSGLEN>0</p2:TOA_MSGLEN></p2:TOA></p0:queryAcctDateResponse>
						if(bodyElement != null){
							builder.append(' ');
							builder.append(bodyElement.getQName());
						}
					}
				}
			} catch (Exception e) {
				
			}
		}
		return builder.toString();
	}
	
	
	private class AxiomAttachmentIterator implements Iterator<Attachment> {
		
		private final Iterator<String> iterator ;
		
		private AxiomAttachmentIterator(){
			iterator = attachments.getContentIDSet().iterator();
		}
		
		public boolean hasNext(){
			return iterator.hasNext();
		}
		
		public Attachment next(){
			String contentId = iterator.next();
			DataHandler dataHandler = attachments.getDataHandler(contentId);
			return new AxiomAttachment(contentId , dataHandler);
		}
		
		public void remove(){
			iterator.remove();
		}
		
	}


	@Override
	public Document getDocument() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void setDocument(Document arg0) {
		// TODO Auto-generated method stub
		
	}



}
